<?php
/**
 * Zend_Session 简化
 * @date 2012-3-1
 * @author GhostWei
 */
class system_session extends system_session_abstract {
	public static $_unitTestEnabled = false;
	protected static $_throwStartupExceptions = true;
	private static $_sessionStarted = false;
	private static $_regenerateIdState = 0;
	private static $_defaultOptions = array(
				'save_path' => null, 
				'name' => null, 
				'save_handler' => null,
				'gc_probability' => null, 
				'gc_divisor' => null, 
				'gc_maxlifetime' => null, 
				'serialize_handler' => null, 
				'cookie_lifetime' => null, 
				'cookie_path' => null, 
				'cookie_domain' => null, 
				'cookie_secure' => null, 
				'cookie_httponly' => null, 
				'use_cookies' => null, 
				'use_only_cookies' => 'on', 
				'referer_check' => null, 
				'entropy_file' => null, 
				'entropy_length' => null, 
				'cache_limiter' => null, 
				'cache_expire' => null, 
				'use_trans_sid' => null, 
				'bug_compat_42' => null, 
				'bug_compat_warn' => null, 
				'hash_function' => null, 
				'hash_bits_per_character' => null
	);
	private static $_localOptions = array(
			'db_lifetime' => '_dbLifetime', 
			'remember_me_seconds' => '_rememberMeSeconds', 
			'throw_startup_exceptions' => '_throwStartupExceptions'
	);
	private static $_writeClosed = false;
	private static $_sessionCookieDeleted = false;
	private static $_destroyed = false;
	private static $_rememberMeSeconds = 1209600;
	private static $_dbLifetime = 10;
	private static $_defaultOptionsSet = false;
	private static $_saveHandler = null;
	
	protected function __construct() {
		
	}
	
	public static function start($options = array()) {		
		if(isset($options['db_handler']) && $options['db_handler']===true) {
			self::setSaveHandler(new system_session_handler_table());
		}
		if(isset($options['db_handler'])) {
			unset($options['db_handler']);
		}
		
		if(self::$_sessionStarted && self::$_destroyed) {
			throw new system_session_exception('The session was explicitly destroyed during this request, attempting to re-start is not allowed.');
		}		
		if(self::$_sessionStarted) {
			return;
		}
		if(!self::$_defaultOptionsSet) {
			self::setOptions(is_array($options) ? $options : array());
		}	
		$filename = $linenum = null;
		if(!self::$_unitTestEnabled && headers_sent($filename, $linenum)) {
			throw new system_session_exception("Session must be started before any output has been sent to the browser;" . " output started in {$filename}/{$linenum}");
		}
		if(!self::$_unitTestEnabled && defined('SID')) {
			throw new system_session_exception('session has already been started by session.auto-start or session_start()');
		}
		
		$errorLevel = (is_int(self::$_throwStartupExceptions)) ? self::$_throwStartupExceptions : E_ALL;
		if(!self::$_unitTestEnabled) {			
			if(self::$_throwStartupExceptions) {
				set_error_handler(array('system_session_exception', 'handleSessionStartError'), $errorLevel);
			}			
			$startedCleanly = session_start();			
			if(self::$_throwStartupExceptions) {
				restore_error_handler();
			}			
			if(!$startedCleanly || system_session_exception::$sessionStartError != null) {
				if(self::$_throwStartupExceptions) {
					set_error_handler(array('system_session_exception', 'handleSilentWriteClose'), $errorLevel);
				}
				session_write_close();
				if(self::$_throwStartupExceptions) {
					restore_error_handler();
					throw new system_session_exception(__CLASS__ . '::' . __FUNCTION__ . '() - ' . system_session_exception::$sessionStartError);
				}
			}
		}
		
		parent::$_readable = true;
		parent::$_writable = true;
		self::$_sessionStarted = true;
		if(self::$_regenerateIdState === -1) {
			self::regenerateId();
		}
	}
	
	public static function setOptions(array $userOptions = array()) {
		if(!self::$_defaultOptionsSet) {
			foreach(self::$_defaultOptions as $defaultOptionName => $defaultOptionValue) {
				if(isset(self::$_defaultOptions[$defaultOptionName])) {
					ini_set("session.$defaultOptionName", $defaultOptionValue);
				}
			}			
			self::$_defaultOptionsSet = true;
		}		
		foreach($userOptions as $userOptionName => $userOptionValue) {			
			$userOptionName = strtolower($userOptionName);
			if(array_key_exists($userOptionName, self::$_defaultOptions)) {
				ini_set("session.$userOptionName", $userOptionValue);
			} elseif(isset(self::$_localOptions[$userOptionName])) {
				self::${self::$_localOptions[$userOptionName]} = $userOptionValue;
			} else {				
				throw new system_session_exception("Unknown option: $userOptionName = $userOptionValue");
			}
		}
	}
	
	public static function getOptions($optionName = null) {
		$options = array();
		foreach(ini_get_all('session') as $sysOptionName => $sysOptionValues) {
			$options[substr($sysOptionName, 8)] = $sysOptionValues['local_value'];
		}
		foreach(self::$_localOptions as $localOptionName => $localOptionMemberName) {
			$options[$localOptionName] = self::${$localOptionMemberName};
		}		
		if($optionName) {
			if(array_key_exists($optionName, $options)) {
				return $options[$optionName];
			}
			return null;
		}		
		return $options;
	}
	
	public static function setSaveHandler(system_session_handler_interface $saveHandler) {
		self::$_saveHandler = $saveHandler;		
		if(self::$_unitTestEnabled) {
			return;
		}		
		session_set_save_handler(
				array(&$saveHandler, 'open'), 
				array(&$saveHandler, 'close'), 
				array(&$saveHandler, 'read'), 
				array(&$saveHandler, 'write'), 
				array(&$saveHandler, 'destroy'), 
				array(&$saveHandler, 'gc')
		);
	}
	
	public static function getSaveHandler() {
		return self::$_saveHandler;
	}
	
	public static function regenerateId() {
		if(!self::$_unitTestEnabled && headers_sent($filename, $linenum)) {			
			throw new system_session_exception("You must call " . __CLASS__ . '::' . __FUNCTION__ . "() before any output has been sent to the browser; output started in {$filename}/{$linenum}");
		}		
		if(self::$_sessionStarted && self::$_regenerateIdState <= 0) {
			if(!self::$_unitTestEnabled) {
				session_regenerate_id(true);
			}
			self::$_regenerateIdState = 1;
		} else {
			self::$_regenerateIdState = -1;
		}
	}
	
	public static function rememberMe($seconds = null) {
		$seconds = (int) $seconds;
		$seconds = ($seconds > 0) ? $seconds : self::$_rememberMeSeconds;		
		self::rememberUntil($seconds);
	}
	
	public static function forgetMe() {
		self::rememberUntil(0);
	}
	
	public static function rememberUntil($seconds = 0) {
		if(self::$_unitTestEnabled) {
			self::regenerateId();
			return;
		}		
		$cookieParams = session_get_cookie_params();		
		session_set_cookie_params($seconds, $cookieParams['path'], $cookieParams['domain'], $cookieParams['secure']);
		self::regenerateId();
	}
	
	public static function sessionExists() {
		if(ini_get('session.use_cookies') == '1' && isset($_COOKIE[session_name()])) {
			return true;
		} elseif(!empty($_REQUEST[session_name()])) {
			return true;
		} elseif(self::$_unitTestEnabled) {
			return true;
		}		
		return false;
	}
	
	public static function isDestroyed() {
		return self::$_destroyed;
	}
	
	public static function isStarted() {
		return self::$_sessionStarted;
	}
	
	public static function isRegenerated() {
		return ((self::$_regenerateIdState > 0) ? true : false);
	}
	
	public static function getId() {
		return session_id();
	}
	
	public static function setId($id) {
		if(!self::$_unitTestEnabled && defined('SID')) {
			throw new system_session_exception('The session has already been started.  The session id must be set first.');
		}
		
		if(!self::$_unitTestEnabled && headers_sent($filename, $linenum)) {
			throw new system_session_exception("You must call " . __CLASS__ . '::' . __FUNCTION__ . "() before any output has been sent to the browser; output started in {$filename}/{$linenum}");
		}
		
		if(!is_string($id) || $id === '') {
			throw new system_session_exception('You must provide a non-empty string as a session identifier.');
		}		
		session_id($id);
	}
	
	public static function stop() {
		parent::$_writable = false;
	}
	
	public static function writeClose($readonly = true) {
		if(self::$_unitTestEnabled) return;		
		if(self::$_writeClosed) return;		
		if($readonly) parent::$_writable = false;		
		session_write_close();
		self::$_writeClosed = true;
	}
	
	public static function destroy($remove_cookie = true, $readonly = true) {
		if(self::$_unitTestEnabled) return;		
		if(self::$_destroyed) return;		
		if($readonly) parent::$_writable = false;		
		session_destroy();
		self::$_destroyed = true;		
		if($remove_cookie) self::expireSessionCookie();
	}
	
	public static function expireSessionCookie() {
		if(self::$_unitTestEnabled)return;		
		if(self::$_sessionCookieDeleted) return;		
		self::$_sessionCookieDeleted = true;		
		if(isset($_COOKIE[session_name()])) {
			$cookie_params = session_get_cookie_params();			
			setcookie(session_name(), false, 315554400, $cookie_params['path'], $cookie_params['domain'], $cookie_params['secure']);
		}
	}
	
	public static function namespaceIsset($namespace) {
		return parent::_namespaceIsset($namespace);
	}
	
	public static function namespaceUnset($namespace) {
		parent::_namespaceUnset($namespace);
		system_session_namespace::resetSingleInstance($namespace);
	}
	
	public static function namespaceGet($namespace) {
		return parent::_namespaceGetAll($namespace);
	}
	
	public static function isWritable() {
		return parent::$_writable;
	}
	
	public static function isReadable() {
		return parent::$_readable;
	}

}
